// 联合类型只能调用被联合对象的共有成员
// 虽然可以通过类型断言的方式 来绕开 ts 的检测机制
// 但是那种写法很麻烦 而且风险极高
// 所以 ts 推出一种新的写法 类型保护 来实现

interface Bird {
  fly(): void
  layEggs(): void
}

interface Fish {
  swim(): void
  layEggs(): void
}

let pet = ((): Fish | Bird => {
  return {
    layEggs() { },
    swim() { },
    fly() { }
  }
})()

// 描述符 pet is Fish 假设 pet 是一个 Fish 类型
// 返回一个布尔值 如果是 true 说明这个假设是对的
// 反之 就是 其他类型
function isFish(pet: Fish | Bird): pet is Fish {
  return (<Fish>pet).swim !== undefined
}

if (isFish(pet)) {
  pet.swim()
} else {
  pet.fly()
}

// typeof 类型保护 不必使用类型断言来绕开检测机制
// 但是只适用于基础数据类型
function isNumber(x: any): x is number {
  return typeof x === 'number'
}

function isString(x: any): x is string {
  return typeof x === 'string'
}

function test(value: string, padding: string | number) {
  if (isNumber(padding)) return Array(padding + 1).join(' ') + value
  if (isString(padding)) return padding + value
  throw new Error('padding参数类型错误！')
}

// instanceof 类型保护 通过构造函数来细化类型
interface Animal {
  run(): void
}
class Cat implements Animal {
  constructor(private name: string) { }
  run(): Cat {
    console.log(this.name + '在优雅的奔跑！');
    return this
  }
  catchMouse() {
    console.log(this.name + '会抓老鼠');
  }
}
class Dog implements Animal {
  constructor(private name: string) { }
  run(): Dog {
    console.log(this.name + '在狂野的奔跑！');
    return this
  }
  protectHome() {
    console.log(this.name + '会看家');
  }
}

// 产生随机动物类型
function createRandomAnimal(name: string): Animal {
  return Math.random() < 0.4 ?
    new Cat(name) :
    new Dog(name)
}

let animal: Animal = createRandomAnimal('卡萨');
// 使用 instanceof 来细化角色
if (animal instanceof Cat) {
  animal.run().catchMouse()
}

if (animal instanceof Dog) {
  animal.run().protectHome()
}

// 类性别名 给某个类型取个别名 只是为了方便使用
type str = string
type num = number
type Tree<T> = {
  value: T,
  left: Tree<T>,
  right: Tree<T>
}
